/*
 * Created on 21-ago-2004
 *
 */
package guru.mercurio.core.delivery;

import guru.mercurio.PassportConfigurationException;
import guru.mercurio.Receptor;
import guru.mercurio.common.AbsoluteApplicationAddress;
import guru.mercurio.common.PhoneAddress;
import guru.mercurio.common.RelativeApplicationAddress;
import guru.mercurio.core.delivery.https.HttpsHostNameVerifier;
import guru.mercurio.core.delivery.https.HttpsX509TrustManager;
import guru.mercurio.data.DataManager;
import guru.mercurio.data.PassportDataManager;
import guru.merlin.core.AbstractEnvelopeProcessor;
import guru.merlin.core.CantHandException;
import guru.merlin.core.EnvelopeCantHandException;
import guru.merlin.core.PeerCantHandException;
import guru.merlin.core.Envelope;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Properties;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.log4j.Logger;

/**
 * @author cobach
 *  
 */
public class DeliveryEP extends AbstractEnvelopeProcessor{
    Logger logger = Logger.getLogger(DeliveryEP.class);

    private DataManager dm;
    private PreDispatchProcessor pp;
    private PassportDataManager pdm; //SANTIAGO VENTURA: MODIFICADO 08/11/2005

    private static String ERROR_RESPONSE = "Su solicitud no pudo ser procesada [m]";

    public DeliveryEP() throws Exception {
        super();
    }

    public void openConnection(String parametersFilePath) throws CantHandException {
        try {
            FileInputStream is = new FileInputStream(parametersFilePath);
            Properties prop = new Properties();
            prop.load(is);

            String driver = prop.getProperty("db.driver");
            String dbmsURL = prop.getProperty("db.dbmsURL");
            String username = prop.getProperty("db.username");
            String password = prop.getProperty("db.password");

            dm = new DataManager(driver, dbmsURL, username, password);
            pp = new PreDispatchProcessor(dm);
            pdm = new PassportDataManager(dm); //SANTIAGO VENTURA: MODIFICADO 08/11/2005
        } catch (Exception e) {
            throw new PeerCantHandException("" + e);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see guru.merlin.core.AbstractEnvelopeProcessor#handEnvelope(guru.merlin.core.Envelope)
     */
    public Object handEnvelope(Envelope envelope) throws CantHandException {
        String msg = "";
        PhoneAddress from;
        String result = null;
        AbsoluteApplicationAddress app;

        if (!(envelope.getOrigin() instanceof PhoneAddress)) { 
        	throw new EnvelopeCantHandException("Can't hand Envelope with origin of type: " + envelope.getOrigin().getClass().getName()); 
        }

        if (!(envelope.getDestination() instanceof RelativeApplicationAddress)) { 
        	throw new EnvelopeCantHandException("Can't hand an Envelope with a destination different from RelativeApplicationAddress"); 
        }

        app = pp.preProcess(envelope);

        from = (PhoneAddress) envelope.getOrigin();
        //SANTIAGO VENTURA: MODIFICADO 08/11/2005
        if(logger.isDebugEnabled()) {
            msg = "Envelope was received to dispatch to the application:" + app.toString() + "with the passport " + app.getPassport();
            logger.debug(msg);
        }  

        if (app.getProtocol().equalsIgnoreCase("WS")) {
            try {
                //SANTIAGO VENTURA: MODIFICADO 08/11/2005
                //logger.info("Web service invocation is being done.");
                result = webServiceInvoke(app.getURL(), app.getKey(), from.getcountrycode(), from.getareacode(), from.getlocal(), envelope.getContent().toString(), "" + envelope.getId() + "");
                logger.info("Web service invocation was successful and reply: " + result);
                if (isSimpleMode(app.getPassport())) {
                    logger.error("The delivery scheme is simple mode. Reply is not expected.");
                    //Se aado el retorno del Envelope 17032006
                    return envelope;
                }                
            } catch (Throwable e) {
                result = ERROR_RESPONSE;
                logger.error("Web service invocation has failed:" + e);
            }
        } else if (app.getProtocol().equalsIgnoreCase("CGI")) {
            try {
                //SANTIAGO VENTURA: MODIFICADO 01/03/2006
                logger.info("Calling to CGI:" + app.getURL());
                result = enviarCGI(app.getURL(), app.getKey(), from.getCountryCode(), from.getAreaCode(), from.getLocal(), envelope.getContent().toString(), "" + envelope.getId() + "");
                logger.info("CGI invocation was successful and reply: " + result);
                if (isSimpleMode(app.getPassport())) {
                    logger.error("The delivery scheme is simple mode. Reply is not expected.");
                    //Se aado el retorno del Envelope 17032006
                    return envelope;
                }     
            } catch (Throwable e) {
                result = ERROR_RESPONSE;
                logger.error("CGI service invocation has failed:" + e);
            }
        } else {
            throw new PassportConfigurationException("The protocol of the application must be defined 'CGI' or 'WS'");
        }

        if (result == null) result = ERROR_RESPONSE;

        
        
        Envelope reply = envelope.replyTo(result.trim());
        reply.getOrigin().setVariable(app.getPassport());
        reply.setComment("Interactivo");
        //Se aado el retorno del Envelope 17032006
        return getEnvelopeHandler().handEnvelope(reply);
    }

    private String enviarCGI(String url, String clave, String codPais, String codArea,  String numRem, String mensaje, String idMensaje) throws Exception{
        String tempResponse = "";
        try {
            String mensajeEncoded=URLEncoder.encode(mensaje);
            String direccionIn = url.concat("?clave="+URLEncoder.encode(clave)+"&codPais="+codPais+"&codArea="+codArea+"&numRem="+numRem+"&mensaje="+mensajeEncoded+"&idMensaje="+idMensaje);
            
            //1. Open the connection and keep it open in the instance of this class
            logger.info("Connection:"+direccionIn);            
            //HttpURLConnection conn;
            URL direccion = new URL(direccionIn);
            //URLConnection conexion = direccion.openConnection();            
            //conexion.setDoOutput(true);
            
            if (url.startsWith("https")){
                javax.net.ssl.HttpsURLConnection conn = (javax.net.ssl.HttpsURLConnection) direccion.openConnection();
        		// Init trust managers so the authenticity of the certificate is in our
        		// control.
        		X509TrustManager oTrustMngr = new HttpsX509TrustManager();
        		TrustManager oEnlistaTrustManagers[] = { oTrustMngr };
        		SSLContext ctx = SSLContext.getInstance("SSL");
        		ctx.init(null, oEnlistaTrustManagers, null);
        		SSLSocketFactory sf = ctx.getSocketFactory();    		
        		conn.setSSLSocketFactory(sf);
        		conn.setHostnameVerifier(new HttpsHostNameVerifier());

        		conn.setDoInput(true);
        		conn.setDoOutput(true);
        		conn.setUseCaches(false);
        		conn.setRequestMethod("GET");
        		InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        		BufferedReader br = new BufferedReader(isr);
        		String temp;
        		while ((temp = br.readLine()) != null)
        			tempResponse = tempResponse + temp;
        		
        		br.close();
        		isr.close();
        		conn.disconnect();
            } else {
                HttpURLConnection conn = (HttpURLConnection)direccion.openConnection();   
        		conn.setRequestMethod("GET");
        		InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        		BufferedReader br = new BufferedReader(isr);
        		String temp;
        		while ((temp = br.readLine()) != null)
        			tempResponse = tempResponse + temp;
        		
        		br.close();
        		isr.close();
        		conn.disconnect();
            }                        		              
        }catch(Exception e){
            e.printStackTrace();
            throw new Exception(e.getMessage());
        }              
        return(tempResponse); 
    }
    

    /**
     * SANTIAGO VENTURA: INCLUIDO 08/11/2005 Este mtodo hace un validacin del esquema de entrega
     * usado por el web service a ser invocado. El esquema de entrega puede ser modo simple (asincrono)
     * o respuesta inmediata o piggyback (sincrono).
     * 
     * @param passport
     * @return false if is piggyback true if is simple mode.
     * @throws Exception
     */
    public boolean isSimpleMode(String passport) throws Exception {
        String msg = "";
        String deliveryScheme = "";
        boolean singleWay = false;
        if (passport == null || passport.equals("")) {
            if(logger.isDebugEnabled()) {
                msg = "The delivery scheme cannot be defined.";
                logger.debug(msg);
            }
            throw new Exception("The delivery scheme cannot be defined.");
        }
        try {
            deliveryScheme = pdm.getDeliverySchema(passport);
            if(logger.isDebugEnabled()) {
                msg = "The delivery scheme is " + deliveryScheme;
                logger.debug(msg);
            }            
        } catch (Exception e) {
            throw new Exception("The delivery scheme cannot be defined for the passport " +passport+" "+e.toString());
        }

        if (deliveryScheme.equalsIgnoreCase("piggyback")) return singleWay;
        else singleWay = true;

        return (singleWay);
    }

    public String webServiceInvoke(String url, String clave, int codPaisRem, int codAreaRem, int numRem, String mensaje, String idMensaje) throws Exception {
        Receptor rec = ReceptorHelper.bind(url);
        return (rec.recibeSMS(clave, codPaisRem, codAreaRem, numRem, mensaje, idMensaje));
    }

    /* (sin Javadoc)
     * @see guru.merlin.core.EnvelopeHandler#retrieveEnvelope(java.lang.Object)
     */
    public void retrieveEnvelope(Object EnvelopeReference) throws CantHandException {
        // TODO Apndice de mtodo generado automticamente
        
    }

    /* (sin Javadoc)
     * @see guru.merlin.core.EnvelopeHandler#commitHandEnvelope(java.lang.Object)
     */
    public void commitHandEnvelope(Object EnvelopeReference) throws CantHandException {
        // TODO Apndice de mtodo generado automticamente
        
    }

}